package org.jeecg.modules.base.service;

import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * UDP设备通讯服务器
 * 实现设备数据接收、协议解析和响应发送
 */
public class DeviceUdpServer {
    private static final int MAX_PACKET_SIZE = 1500;
    private static final byte FRAME_DELIMITER = 0x7E;
    private static final byte ESCAPE_CHAR = 0x5E;
    private static final byte ESCAPED_7E = 0x7D;
    private static final byte ESCAPED_5E = 0x5D;

    private final int port;
    private volatile boolean running;
    private DatagramSocket socket;
    private ExecutorService executor;

    // 回调接口用于处理业务逻辑
    private final DeviceDataHandler dataHandler;

    public DeviceUdpServer(int port, DeviceDataHandler dataHandler) {
        this.port = port;
        this.dataHandler = dataHandler;
    }

    /**
     * 启动UDP服务器
     */
    public void start() throws IOException {
        if (running) {
            throw new IllegalStateException("Server is already running");
        }

        socket = new DatagramSocket(port);
        running = true;
        executor = Executors.newFixedThreadPool(4); // 使用线程池处理请求

        System.out.println("UDP Server started on port " + port);

        // 启动接收线程
        new Thread(this::receiveLoop).start();
    }

    /**
     * 停止服务器
     */
    public void stop() {
        running = false;
        if (socket != null) {
            socket.close();
        }
        if (executor != null) {
            executor.shutdownNow();
        }
        System.out.println("UDP Server stopped");
    }

    /**
     * 接收循环
     */
    private void receiveLoop() {
        byte[] buffer = new byte[MAX_PACKET_SIZE];

        while (running) {
            try {
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                socket.receive(packet);

                // 处理接收到的数据包（使用线程池）
                executor.execute(() -> processPacket(packet));
            } catch (SocketException e) {
                if (running) {
                    System.err.println("Socket closed unexpectedly: " + e.getMessage());
                }
            } catch (IOException e) {
                System.err.println("Error receiving packet: " + e.getMessage());
            }
        }
    }

    /**
     * 处理接收到的数据包
     */
    private void processPacket(DatagramPacket packet) {
        try {
            // 提取原始数据
            byte[] rawData = Arrays.copyOfRange(packet.getData(), 0, packet.getLength());
            InetAddress clientAddress = packet.getAddress();
            int clientPort = packet.getPort();

            System.out.println("Received raw data from " + clientAddress + ":" + clientPort +
                    " - " + bytesToHex(rawData));

            // 处理数据
            byte[] processedData = processReceivedData(rawData);

            // 提取设备信息
            DeviceInfo deviceInfo = extractDeviceInfo(processedData);

            // 调用业务处理器
            byte[] responsePayload = dataHandler.handleDeviceData(deviceInfo, processedData);

            // 发送响应
            if (responsePayload != null) {
                sendResponse(clientAddress, clientPort, processedData[10], responsePayload);
            }
        } catch (Exception e) {
            System.err.println("Error processing packet: " + e.getMessage());
        }
    }

    /**
     * 处理接收到的数据
     */
    private byte[] processReceivedData(byte[] rawData) {
        // 1. 检查帧头帧尾
        if (rawData[0] != FRAME_DELIMITER || rawData[rawData.length - 1] != FRAME_DELIMITER) {
            throw new IllegalArgumentException("Invalid frame boundaries");
        }

        // 2. 提取中间数据并反转义
        byte[] unescaped = unescapeData(Arrays.copyOfRange(rawData, 1, rawData.length - 1));

        // 3. 验证CRC
        verifyCrc(unescaped);

        // 4. 返回有效载荷 (去掉CRC字节)
        return Arrays.copyOf(unescaped, unescaped.length - 2);
    }

    /**
     * 反转义处理
     */
    private byte[] unescapeData(byte[] data) {
        ByteBuffer out = ByteBuffer.allocate(data.length);
        int i = 0;

        while (i < data.length) {
            if (data[i] == ESCAPE_CHAR) {
                if (i + 1 >= data.length) {
                    throw new IllegalArgumentException("Invalid escape sequence");
                }

                if (data[i + 1] == ESCAPED_5E) {
                    out.put((byte) 0x5E);
                } else if (data[i + 1] == ESCAPED_7E) {
                    out.put((byte) 0x7E);
                } else {
                    throw new IllegalArgumentException("Unknown escape sequence");
                }
                i += 2;
            } else {
                out.put(data[i]);
                i++;
            }
        }

        return Arrays.copyOf(out.array(), out.position());
    }

    /**
     * 验证CRC
     */
    private void verifyCrc(byte[] data) {
        int receivedCrc = ((data[data.length - 1] & 0xFF) << 8) | (data[data.length - 2] & 0xFF);
        int calculatedCrc = calculateCrc(data, 0, data.length - 2);

        if (receivedCrc != calculatedCrc) {
            throw new IllegalArgumentException("CRC validation failed. Expected: " +
                    Integer.toHexString(calculatedCrc) + ", Received: " +
                    Integer.toHexString(receivedCrc));
        }
    }

    /**
     * 计算CRC16 (CCITT-FALSE算法)
     */
    private int calculateCrc(byte[] data, int offset, int length) {
        int crc = 0xFFFF;

        for (int i = offset; i < offset + length; i++) {
            crc ^= (data[i] & 0xFF) << 8;
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x8000) != 0) {
                    crc = (crc << 1) ^ 0x1021;
                } else {
                    crc <<= 1;
                }
            }
            crc &= 0xFFFF;
        }

        return crc;
    }

    /**
     * 从数据包中提取设备信息
     */
    private DeviceInfo extractDeviceInfo(byte[] data) {
        // 协议格式：
        // 位置 0: 帧头 (已处理)
        // 位置 1: 协议类型 (0x03 表示以太网)
        // 位置 2-5: 站点ID (小端序)
        // 位置 6: 设备ID
        // 位置 7-8: 包序列号

        ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);

        // 跳过协议类型
        bb.get();

        int stationId = bb.getInt();
        byte equipmentId = bb.get();
        int sequence = bb.getShort() & 0xFFFF;

        return new DeviceInfo(stationId, equipmentId, sequence);
    }

    /**
     * 发送响应给设备
     */
    public void sendResponse(InetAddress address, int port, byte commandType, byte[] payload)
            throws IOException {

        // 1. 构建基础数据包
        byte[] basePacket = buildBasePacket(commandType);

        // 2. 添加有效载荷
        byte[] fullPacket = new byte[basePacket.length + payload.length];
        System.arraycopy(basePacket, 0, fullPacket, 0, basePacket.length);
        System.arraycopy(payload, 0, fullPacket, basePacket.length, payload.length);

        // 3. 计算并添加CRC校验
        byte[] packetWithCrc = addCrc(fullPacket);

        // 4. 转义处理
        byte[] escapedData = escapeData(packetWithCrc);

        // 5. 添加帧头帧尾
        byte[] finalPacket = wrapFrame(escapedData);

        // 6. 通过UDP发送
        DatagramPacket packet = new DatagramPacket(
                finalPacket, finalPacket.length, address, port
        );

        socket.send(packet);

        System.out.println("Sent response to " + address + ":" + port +
                " - " + bytesToHex(finalPacket));
    }

    /**
     * 构建基础数据包
     */
    private byte[] buildBasePacket(byte commandType) {
        ByteBuffer bb = ByteBuffer.allocate(14);
        bb.order(ByteOrder.LITTLE_ENDIAN);

        // 包头
        bb.put(FRAME_DELIMITER);
        bb.put((byte) 0x03);  // 表示以太网/互联网

        // 站点ID (4字节小端序) - 使用示例值
        bb.putInt(0x12345678);

        // 设备ID - 使用示例值
        bb.put((byte) 0x01);

        // 包序列号 - 使用示例值
        bb.putShort((short) 0x0001);

        // 固定头
        bb.put((byte) 0x80);
        bb.put((byte) 0x01);

        // 命令类型
        bb.put(commandType);
        bb.put((byte) 0xFF);  // 预留

        return bb.array();
    }

    /**
     * 添加CRC校验
     */
    private byte[] addCrc(byte[] data) {
        // 计算CRC (从第二个字节开始到数据结尾)
        int crc = calculateCrc(data, 1, data.length - 1);

        ByteBuffer bb = ByteBuffer.allocate(data.length + 2);
        bb.put(data);
        bb.put((byte) (crc & 0xFF));        // CRC低字节
        bb.put((byte) ((crc >> 8) & 0xFF)); // CRC高字节

        return bb.array();
    }

    /**
     * 数据转义处理
     */
    private byte[] escapeData(byte[] data) {
        ByteBuffer out = ByteBuffer.allocate(data.length * 2); // 最坏情况下大小翻倍

        for (byte b : data) {
            switch (b) {
                case 0x5E:
                    out.put(ESCAPE_CHAR);
                    out.put(ESCAPED_5E);
                    break;
                case 0x7E:
                    out.put(ESCAPE_CHAR);
                    out.put(ESCAPED_7E);
                    break;
                default:
                    out.put(b);
            }
        }

        return Arrays.copyOf(out.array(), out.position());
    }

    /**
     * 添加帧头帧尾
     */
    private byte[] wrapFrame(byte[] data) {
        ByteBuffer bb = ByteBuffer.allocate(data.length + 2);
        bb.put(FRAME_DELIMITER);
        bb.put(data);
        bb.put(FRAME_DELIMITER);
        return bb.array();
    }

    /**
     * 字节数组转十六进制字符串 (用于调试)
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b));
        }
        return sb.toString().trim();
    }

    /**
     * 设备信息容器类
     */
    public static class DeviceInfo {
        private final int stationId;
        private final byte equipmentId;
        private final int sequence;

        public DeviceInfo(int stationId, byte equipmentId, int sequence) {
            this.stationId = stationId;
            this.equipmentId = equipmentId;
            this.sequence = sequence;
        }

        public int getStationId() {
            return stationId;
        }

        public byte getEquipmentId() {
            return equipmentId;
        }

        public int getSequence() {
            return sequence;
        }

        @Override
        public String toString() {
            return String.format("Station: 0x%08X, Equipment: 0x%02X, Seq: %d",
                    stationId, equipmentId & 0xFF, sequence);
        }
    }

    /**
     * 设备数据处理接口
     */
    public interface DeviceDataHandler {
        /**
         * 处理设备数据并返回响应
         *
         * @param deviceInfo 设备信息
         * @param data 处理后的设备数据
         * @return 要发送给设备的响应数据，如果不需要响应则返回null
         */
        byte[] handleDeviceData(DeviceInfo deviceInfo, byte[] data);
    }

    /**
     * 示例服务器启动方法
     */
    public static void main(String[] args) {
        int port = 5000;

        // 创建数据处理实现
        DeviceDataHandler handler = (deviceInfo, data) -> {
            System.out.println("Handling data from: " + deviceInfo);
            System.out.println("Data: " + bytesToHex(data));

            // 示例：根据命令类型返回不同响应
            byte commandType = data[10];

            switch (commandType) {
                case 0x02: // 查询命令
                    System.out.println("Processing query command");
                    return new byte[]{0x01, 0x02, 0x03}; // 示例响应数据
                case 0x03: // 设置命令
                    System.out.println("Processing set command");
                    return new byte[]{0x00}; // 成功响应
                default:
                    System.out.println("Unknown command: 0x" + Integer.toHexString(commandType & 0xFF));
                    return null; // 不响应
            }
        };

        // 创建并启动服务器
        DeviceUdpServer server = new DeviceUdpServer(port, handler);
        try {
            server.start();

            // 保持服务器运行
            System.out.println("Press Enter to stop the server...");
            System.in.read();

        } catch (IOException e) {
            System.err.println("Server error: " + e.getMessage());
        } finally {
            server.stop();
        }
    }
}